package com.zongtui.fourinone.base;

import com.zongtui.fourinone.base.config.ConfigContext;
import com.zongtui.fourinone.cache.Cache;
import com.zongtui.fourinone.cache.CacheFacade;
import com.zongtui.fourinone.cache.CacheLocal;
import com.zongtui.fourinone.cache.CacheProxy;
import com.zongtui.fourinone.contractor.CtorLocal;
import com.zongtui.fourinone.coolhash.CoolHashClient;
import com.zongtui.fourinone.coolhash.CoolHashCtor;
import com.zongtui.fourinone.delegate.DelegateConsole;
import com.zongtui.fourinone.file.FileAdapter;
import com.zongtui.fourinone.file.StartResult;
import com.zongtui.fourinone.file.dump.DumpCtor;
import com.zongtui.fourinone.fttp.*;
import com.zongtui.fourinone.obj.ObjectBean;
import com.zongtui.fourinone.park.*;
import com.zongtui.fourinone.utils.log.LogUtil;
import com.zongtui.fourinone.worker.*;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.rmi.RemoteException;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 基本入口类.
 */
public class BeanContext extends ServiceContext {
    /**
     * 设置配置文件位置.
     *
     * @param configFile 配置文件位置.
     */
    public static void setConfigFile(String configFile) {
        ConfigContext.configFile = configFile;
    }

    /**
     * 获取服务.
     *
     * @param host        主机
     * @param port        端口
     * @param serviceName 服务名
     * @param servers     服务器列表
     * @return 服务对象
     */
    public static ParkLocal getPark(String host, int port, String serviceName, String[][] servers) {
        return DelegateConsole.bind(ParkLocal.class, new ParkProxy(host, port, servers, serviceName));
    }

    /**
     * 获取服务.
     *
     * @param host    主机
     * @param port    端口
     * @param servers 服务器列表
     * @return 服务对象
     */
    public static ParkLocal getPark(String host, int port, String[][] servers) {
        return getPark(host, port, ConfigContext.getParkService(), servers);
    }

    /**
     * 获取服务.
     *
     * @return 服务对象
     */
    public static ParkLocal getPark() {
        String[][] parkConfig = ConfigContext.getParkConfig();
        return getPark(parkConfig[0][0], Integer.parseInt(parkConfig[0][1]), parkConfig);
    }

    /**
     * 获取服务.
     *
     * @param host 主机
     * @param port 端口
     * @return 服务对象.
     */
    public static ParkLocal getPark(String host, int port) {
        String[][] parkConfig = ConfigContext.getParkConfig();
        parkConfig[0][0] = host != null ? host : parkConfig[0][0];
        parkConfig[0][1] = port != 0 ? (port + "") : parkConfig[0][1];
        return getPark(host, port, parkConfig);
    }

    /**
     * 启动服务.
     *
     * @param host        主机
     * @param port        端口
     * @param serviceName 服务名
     * @param servers     服务器列表
     */
    public static void startPark(String host, int port, String serviceName, String[][] servers) {
        try {
            startService(host, port, serviceName, new ParkService(host, port, servers, serviceName));
            if (Boolean.valueOf(ConfigContext.getConfig("park", "startWebApp", null, "false"))) {
                startInternetServer();
            }
        } catch (RemoteException e) {//new ParkService throw
            LogUtil.info("[BeanContext]", "[startPark]", e);
//            e.printStackTrace();
        }
    }

    /**
     * 启动服务.
     *
     * @param host    主机
     * @param port    端口
     * @param servers 服务器列表
     */
    public static void startPark(String host, int port, String[][] servers) {
        startPark(host, port, ConfigContext.getParkService(), servers);
    }

    /**
     * 启动服务.
     *
     * @param host 主机
     * @param port 端口
     */
    public static void startPark(String host, int port) {
        String[][] parkcfg = ConfigContext.getParkConfig();
        parkcfg[0][0] = host != null ? host : parkcfg[0][0];
        parkcfg[0][1] = port != 0 ? (port + "") : parkcfg[0][1];
        startPark(host, port, parkcfg);
    }

    /**
     * 启动服务.
     */
    public static void startPark() {
        String[][] parkConfig = ConfigContext.getParkConfig();
        startPark(parkConfig[0][0], Integer.parseInt(parkConfig[0][1]), parkConfig);
    }

    /**
     * 启动远程服务.
     */
    public static void startInternetServer() {
        String[] inetcfg = ConfigContext.getInetConfig();
        ParkInetServer.start(inetcfg[0], Integer.parseInt(inetcfg[1]), 0);
    }

    /**
     * 启动工人节点.
     *
     * @param host          域名
     * @param port          端口号
     * @param serviceName   服务名称
     * @param migrantWorker 工人对象
     */
    public static void startWorker(String host, int port, String serviceName, MigrantWorker migrantWorker) {
        //启动Worker对象.
        startWorker(host, port, serviceName, migrantWorker, false);
    }

    /**
     * 启动工人节点.
     *
     * @param serviceName   服务名称
     * @param migrantWorker 工人对象
     */
    static void startWorker(String serviceName, MigrantWorker migrantWorker) {
        //默认的工人的配置.
        String[] workerConfig = ConfigContext.getWorkerConfig();

        startWorker(workerConfig[0], Integer.parseInt(workerConfig[1]), serviceName, migrantWorker);
    }

    /**
     * 启动工人节点.
     *
     * @param host          域名
     * @param port          端口号
     * @param serviceName   服务名称
     * @param migrantWorker 工人对象
     * @param isStartUp     是否已经启动
     */
    public static void startWorker(String host, int port, String serviceName, MigrantWorker migrantWorker, boolean isStartUp) {
        try {
            //获得远程启动节点对象.
            ParkActive pa = DelegateConsole.bind(Worker.class, new WorkerService(migrantWorker));
            if (isStartUp) {
                startService(host, port, serviceName, pa, ConfigContext.getInternetStrConfig(migrantWorker.getWorkerJar()), ConfigContext.getPolicyConfig());
            } else {
                startService(host, port, serviceName, pa);
            }

        } catch (RemoteException e) {
            LogUtil.info("[BeanContext]", "[startWorker]", e);
        }
    }

    /**
     *
     * @param host
     * @param port
     * @param sn
     * @param mwk
     */
    public static void startFttpWorker(String host, int port, String sn, MigrantWorker mwk) {
        try {
            ParkActive pa = (ParkActive) DelegateConsole.bind(new Class[]{Worker.class, FttpWorker.class}, new FttpWorkerService(mwk));//Worker.class
            //ConfigContext.getInternetStrConfig(mwk.getWorkerJar())
            startService(host, port, sn, pa);
        } catch (RemoteException e) {
            LogUtil.info("[BeanContext]", "[startFttpWorker]", e);
        }
    }

    public static void startFttpServer() {//root
        String[] fttpcfg = ConfigContext.getFttpConfig();
        startFttpServer(fttpcfg[0], Integer.parseInt(fttpcfg[1]));
    }

    public static void startFttpServer(String host) {
        startFttpServer(host, FttpMigrantWorker.FTTPPORT);
    }

    public static void startFttpServer(String host, int port) {
        new FttpMigrantWorker().waitWorking(host, port, FttpMigrantWorker.FTTPSN);
    }

    public static Worker getWorker(String host, int port, String sn) {
        return getService(Worker.class, host, port, sn);
    }

    public static WorkerLocal getWorkerLocal(String host, int port, String sn) {
        return (WorkerLocal) DelegateConsole.bind(new Class[]{WorkerLocal.class, CtorLocal.class}, new WorkerServiceProxy(host, port, sn));
    }

    public static WorkerLocal getWorkerLocal(String domainnodekey) {
        return DelegateConsole.bind(WorkerLocal.class, new WorkerParkProxy(domainnodekey));
    }

    public static WorkerLocal getWorkerLocal() {
        return (WorkerLocal) DelegateConsole.bind(new Class[]{WorkerLocal.class}, new WorkerLocalProxy());
    }

    public static WorkerLocal getFttpLocal(String host, int port, String sn) {
        return (WorkerLocal) DelegateConsole.bind(new Class[]{WorkerLocal.class, CtorLocal.class, FttpLocal.class}, new FttpWorkerProxy(host, port, sn));
    }

    public static Workman getWorkman(String host, int port, String sn) {
        return DelegateConsole.bind(Workman.class, new WorkerServiceProxy(host, port, sn));
    }

	/*public static <I extends Remote> void start(String host, int port, String sn, I i)
    {
		try{
			BeanService.putBean(host,true,port,sn,i);
		}catch(RemoteException e){
			e.printStackTrace();
		}
	}
	
	public static <I extends Remote> I get(Class<I> a, String host, int port, String sn){
		I i=null;
		try{
			i=(I)BeanService.getBean(host,port,sn);
		}catch(RemoteException e){
			System.out.println(e);
		}
		return i;
	}*/

    public static void startCacheFacade(String host, int port) {
        try {
            startService(host, port, ConfigContext.getCacheFacadeService(), DelegateConsole.bind(Cache.class, new CacheFacade(ConfigContext.getCacheService(), ConfigContext.getCacheGroupConfig())));
        } catch (RemoteException e) {
            LogUtil.info("[BeanContext]", "[startCacheFacade]", e);
            //e.printStackTrace();
        }
        /*try{
            BeanService.putBean(host,true,port,"CacheService",new CacheFacade());//input Groups
		}catch(RemoteException e){
			e.printStackTrace();
		}*/
    }

    public static void startCacheFacade() {
        String[] cachecfg = ConfigContext.getCacheFacadeConfig();
        startCacheFacade(cachecfg[0], Integer.parseInt(cachecfg[1]));
    }

    public static Cache getCacheFacade(String host, int port) {
        return getService(Cache.class, host, port, ConfigContext.getCacheFacadeService());
    }

    //<T> Class BeanContextBase
    public static Cache getCacheFacade() {//loadbalance server host and port
        String[] cachecfg = ConfigContext.getCacheFacadeConfig();
        return getCacheFacade(cachecfg[0], Integer.parseInt(cachecfg[1]));
        //return DelegateConsole.bind(Cache.class, new CacheFacade());
        /*
        Cache ca=null;
		try{
			ca=(Cache)BeanService.getBean(host,port,"CacheService");
		}catch(RemoteException e){
			//e.printStackTrace();
			System.out.println(e);
		}
		return ca;
		*/
    }

    public static CacheLocal getCache() {
        String[] cachecfg = ConfigContext.getCacheFacadeConfig();
        return getCache(cachecfg[0], Integer.parseInt(cachecfg[1]));
    }

    public static CacheLocal getCache(String host, int port) {
        return DelegateConsole.bind(CacheLocal.class, new CacheProxy(host, port));
        //return DelegateHandle.bind(CacheLocal.class, CacheProxy.class);
    }

    public static void startCache(String host, int port, String[][] servers) {
        startPark(host, port, ConfigContext.getCacheService(), servers);
    }

    public static void startCache() {
        String[][] cachecfg = ConfigContext.getCacheConfig();
        startCache(cachecfg[0][0], Integer.parseInt(cachecfg[0][1]), cachecfg);
    }

    public static void exit() {
        close();
    }

    public static int start(String... params) {
        return start(null, null, params);
    }

    public static int start(FileAdapter fa, String... params) {
        return start(null, fa, params);
    }

    public static int start(Map env, String... params) {
        return start(env, null, params);
    }

    public static int start(Map env, FileAdapter fa, String... params) {
        ProcessBuilder pb = new ProcessBuilder(params);
        pb.redirectErrorStream(true);
        int exitcode = -1;
        if (env != null)
            pb.environment().putAll(env);
        if (fa != null)
            pb.directory(fa);
        try {
            Process p = pb.start();
            BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line = "";
            while ((line = stdout.readLine()) != null) {
                System.out.println(line);
            }
            stdout.close();
            //System.out.println("waitFor:"+p.waitFor());
            //System.out.println("exitValue:"+p.exitValue());
            exitcode = p.waitFor();
        } catch (Exception ex) {
            LogUtil.info("[BeanContext]", "[start]", ex);
        }
        return exitcode;
    }


    public static StartResult<Integer> tryStart(String... params) {
        return tryStart(null, null, params);
    }

    public static StartResult<Integer> tryStart(FileAdapter fa, String... params) {
        return tryStart(null, fa, params);
    }

    public static StartResult<Integer> tryStart(Map env, String... params) {
        return tryStart(env, null, params);
    }

    public static StartResult<Integer> tryStart(Map env, FileAdapter fa, String... params) {
        ProcessBuilder pb = new ProcessBuilder(params);
        pb.redirectErrorStream(true);
        if (env != null)
            pb.environment().putAll(env);
        if (fa != null)
            pb.directory(fa);
        try {
            return new StartResult(pb.start(), false);
        } catch (Exception ex) {
            LogUtil.info("[BeanContext]", "[startup]", ex);
        }
        return null;
    }

    public static void killOnExit(StartResult<Integer>[] srarr) {
        killOnExit(srarr, "application will exit...");
    }

    public static void killOnExit(final StartResult<Integer>[] srarr, final String info) {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                LogUtil.info("[StartResult]", "[Exit]", info);
                for (StartResult<Integer> sr : srarr)
                    sr.kill();
            }
        });
    }

    public static ObjectBean getLock() {
        return getLock("_lock", "_lock");
    }

    public static ObjectBean getLock(String lockNode, String lockName) {
        ParkLocal pl = ParkPatternExector.getParkLocal();
        ObjectBean ob = pl.create(lockNode, lockName);
        String nodename = ob.getNode();
        while (true) {
            List<ObjectBean> oblist = pl.get(lockNode);
            String curnode = oblist.get(0).getNode();
            if (curnode.equals(nodename))
                break;
        }
        return ob;

    }


    public static void unLock(ObjectBean ob) {
        ParkPatternExector.getParkLocal().delete(ob.getDomain(), ob.getNode());
    }

    public static String getNumber() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    public static boolean getWindows() {
        return System.getProperty("os.name").contains("Windows");
    }

    private static boolean getLinux() {
        return System.getProperty("os.name").contains("Linux");
    }

    public static void startCoolHashServer(String parkhost, int parkport, int worknum) {
        startCoolHashServer("com.zongtui.fourinone.coolhash.CoolHashWorker", parkhost, parkport, worknum, "fourinone.jar" + File.pathSeparator, "log", null);
    }

    public static void startCoolHashServer(String parkhost, int parkport, int worknum, String jarclasspath, String logdir, String langCode) {
        startCoolHashServer("com.zongtui.fourinone.coolhash.CoolHashWorker", parkhost, parkport, worknum, jarclasspath, logdir, langCode);
    }

    public static void startCoolHashServer(String parkhost, int parkport, int worknum, String langCode) {
        startCoolHashServer("com.zongtui.fourinone.coolhash.CoolHashWorker", parkhost, parkport, worknum, "fourinone.jar" + File.pathSeparator, "log", langCode);
    }

    static void startCoolHashServerLocal(String parkhost, int parkport, int worknum) {
        startCoolHashServer("com.zongtui.fourinone.file.dump.DumpWorker", parkhost, parkport, worknum, "fourinone.jar" + File.pathSeparator, "log", null);
    }

    static void startCoolHashServerLocal(String parkhost, int parkport, int worknum, String jarclasspath, String logdir) {
        startCoolHashServer("com.zongtui.fourinone.file.dump.DumpWorker", parkhost, parkport, worknum, jarclasspath, logdir, null);
    }

    private static void startCoolHashServer(String service, String parkhost, int parkport, int worknum, String jarclasspath, String logdir, String langCode) {
        try {
            final StartResult[] starts = new StartResult[worknum + 1];
            System.out.println("Start ParkService and waiting 4 seconds...");
            starts[0] = BeanContext.tryStart("java", "-cp", jarclasspath, "BeanContext", parkhost, parkport + "");
            starts[0].print(logdir + "/park.log");
            Thread.sleep(4000);
            System.out.println("Start CoolHashService and waiting 5 seconds...");
            int port = parkport + 5;
            for (int i = 1; i < starts.length; i++) {
                if (langCode != null)
                    starts[i] = BeanContext.tryStart("java", "-Dfile.encoding=" + langCode, "-cp", jarclasspath, service, parkhost, parkport + "", parkhost, (port++) + "");
                else
                    starts[i] = BeanContext.tryStart("java", "-cp", jarclasspath, service, parkhost, parkport + "", parkhost, (port++) + "");
                starts[i].print(logdir + "/worker" + i + ".log");
            }
            Thread.sleep(5000);
            System.out.println("Please try connect to CoolHashServer now.");
            BeanContext.killOnExit(starts, "CoolHashServer will exit...");
        } catch (Exception ex) {
            LogUtil.info("[Start]", "[CoolHashServer]", ex);
        }
    }

    public static CoolHashClient getCoolHashClient(String parkhost, int parkport) {
        return new CoolHashCtor(parkhost, parkport);
    }

    static CoolHashClient getCoolHashClientLocal(String parkhost, int parkport) {
        return new DumpCtor(parkhost, parkport);
    }

    public static void main(String[] args) {
        if (args != null && args.length == 2)
            startPark(args[0], Integer.parseInt(args[1]));
        else startPark();
    }
}